package Client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import Client.ClientDetails;
import Messages.AddToWatcherMsg;
import Messages.FileChangeMsg;
import Messages.FinishWatchMsg;
import Messages.Msg;
import Messages.MsgType;
import Messages.SendFileMsg;
import Messages.StartConnectionMsg;
import Messages.StopMsg;
import Watcher.watchService;


/**
 * client protocol is the communication with the server about all the messages that the client
 * want to transfer to the server, like creating or deleting new files
 * @author liel && guy
 *
 */
public class ClientProtocol {

	ClientDetails clientDetails;
	Socket requestSocket;
	ObjectOutputStream out;
 	ObjectInputStream in;
 	Msg response;
 	Msg request;
 	String filePath;
 	String fileCheckSum;
 	int serverPort;
 	Manager manager;
 	String serverIp="localhost";
 	
 	/**
 	 * client protocol constructor. does all the transfer from the client to the 
 	 * server, including open a socket to the server. 
 	 * @param cd - the client details of the current user
 	 */
 	public ClientProtocol(ClientDetails cd) {
 		clientDetails = cd;
 		System.out.println("in client proto"); 		
 		serverPort = 8989;
 		manager = Manager.getInstance();
	}
 	/**
 	 * this method is responsible for all the talking between the
 	 * client and the server about transfer messages from the client to
 	 * the server only!
 	 */
	public void run()
	{
		try{
			//1. creating a socket to connect to the server
			requestSocket = new Socket(serverIp, serverPort);
			System.out.println("Connected to "+serverIp+ " in port "+serverPort);
			//2. get Input and Output streams
			out = new ObjectOutputStream(requestSocket.getOutputStream());
			out.flush();
			in = new ObjectInputStream(requestSocket.getInputStream());
			//3: Communicating with the server
			do
			{
				try
				{
					//Read the response
					response = (Msg)in.readObject();
					System.out.println("server>" + response.getType());
					//The new request
					switch(response.getType())
					{
						case START:
							//if server send start send msg with your details
							request = new StartConnectionMsg(clientDetails);
							sendMessage(request);
							break;
						case WHITCH_ACTION:
							//server every time send which user, and we send the message in the manager's queue 
							Msg m = manager.getMsg();
							if (m.getType()==MsgType.CREATE_FOLDER ||m.getType()==MsgType.SHARE || m.getType()==MsgType.NEW_INFO || m.getType()==MsgType.STOP){
								request = m;
							}
							else{
								/*this is create or delete or modify, and we need to update check sum
								we send only the check sum in the message, because the file can be in the 
								server already*/
								FileChangeMsg f = (FileChangeMsg)m;
								this.fileCheckSum= f.getFileHash();
								if (this.fileCheckSum ==null)
									request = new StopMsg();
								else{
									this.filePath = f.getFileFullPath();							
									request=f;
								}
							}
							sendMessage(request);
							break;
						case IS_NOT_EXIST:
							//the file is not in the server, need to transfer the file
							System.out.println("in client - the file is not exist - send the file to server");
							//in the SendFileMsg we converting the file to bytes
							request = new SendFileMsg(filePath, this.fileCheckSum);
							sendMessage(request);
							break;
						case WATCH:
							//need to add the folder to watcher, it come ofter creating new folder
							AddToWatcherMsg watchMsg=(AddToWatcherMsg)response;
							watchService.getWsInstance().addFolderToListen(clientDetails.getMainFolder()+"\\"
									+watchMsg.getFolderName());
							request=new FinishWatchMsg();
							sendMessage(request);
							break;
						case BAD_DETAILS:
							//if user name or password incorrect, send stop
							System.out.println("userName or Password incorrent");
							request=new StopMsg();
							sendMessage(request);
							break;					
					}
				}
				catch(ClassNotFoundException classNot)
				{
					System.err.println("data received in unknown format");
				}
			}while(request.getType()!=MsgType.STOP);
		}
		catch(UnknownHostException unknownHost)
		{
			System.err.println("You are trying to connect to an unknown host!");
		}
		catch(IOException ioException)
		{
			ioException.printStackTrace();
		}
		finally
		{
			//4: Closing connection
			try
			{
				Thread.sleep(1000);
				in.close();
				out.close();
				requestSocket.close();
			}
			catch(IOException ioException)
			{
				ioException.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	private void sendMessage(Msg msg)
	{
		try{
			out.writeObject(msg);
			out.flush();
			System.out.println("client>" + msg.getType());
		}
		catch(IOException ioException){
			ioException.printStackTrace();
		}
	}
}
